﻿using System.Collections.Generic;
using OverStore.Exceptions;
using OverStore.Runtime.ObjectRepository;
using OverStore.Runtime.Storage;
using OverStore.Scoping;

namespace OverStore.Runtime.PersistenceServiceProvider
{
    /// <summary>
    /// Provide access to different kinds of persistence services: <see cref="IEntityRegistry{TInstance, TIdentifier}"/>, 
    /// <see cref="IRepository{TInstance}"/> using type of instance persisted.
    /// </summary>
    /// <remarks>
    /// <para>
    /// This interface is very important thing, it is the way how repository or object reader for objects of one type 
    /// uses persistence of instances of another types. It allows to obtain service for operate with 
    /// instance identifiers, extract instance by identifier, load and save instance etc.
    /// </para>
    /// <para>
    /// For obtaining services caller must know type of instance. For obtaining services works with instance 
    /// identifier, type of identifier is also required.
    /// </para>
    /// <para>
    /// Different implementations of <see cref="IPersistenceServiceProvider"/> uses different algorithms for define actual
    /// <see cref="IEntityRegistry{TInstance, TIdentifier}"/>, <see cref="IRepository{TInstance}"/> for specified instance type.
    /// </para>
    /// </remarks>
    public interface IPersistenceServiceProvider : IRegistry
    {
        #region Repository members

        /// <summary>
        ///     <para> Updates data of specified instance using data extracted from data storage, reload instance aggregates and
        /// mark instance as unchanged, so further saving will not cause data storage updating (except if object was changed).
        /// </para>
        ///     <para> If specified <paramref name="instance"/> is not registered in repository, method also registers it.
        /// If deffer loading is supported and enabled, the object will only be registered for
        /// loading, and loading itself will be performed by request.</para>
        /// </summary>
        /// <param name="instance">The instance to reload.</param>
        /// <exception cref="IdentityReferenceIntegrityException">Different instance with same  already registered.</exception>
        /// <exception cref="PersistingRuntimeException">Error is occured during reload object data.</exception>
        bool Reload<TInstance>(TInstance instance)
            where TInstance : class;

        /// <summary>
        /// Clears collection and fills it up with object read from parameterless reader for instance.
        /// </summary>
        /// <typeparam name="TInstance">The type of the instance.</typeparam>
        /// <param name="collection">The collection.</param>
        /// <exception cref="IdentityReferenceIntegrityException">Collection contains instance of object with same key already registered.</exception>
        /// <exception cref="PersistingRuntimeException">Error is occured during reload object data.</exception>
        /// <exception cref="PersistenceOperationIsNotSupportedException">Operation is not supported because of configuration part responsible for this operation is missing or does not support specific functionallity.</exception>
        void ReloadCollection<TInstance>(IEnumerable<TInstance> collection)
            where TInstance : class;

        /// <summary>
        /// Clears collection and fills it up with object read from parameterless reader for instance.
        /// </summary>
        /// <typeparam name="TInstance">The type of the instance.</typeparam>
        /// <typeparam name="TParameter">The type of the parameter.</typeparam>
        /// <param name="collection">The collection.</param>
        /// <param name="parameter">The parameter used for get data for loading.</param>
        /// <exception cref="IdentityReferenceIntegrityException">Collection contains instance of object with same key already registered.</exception>
        /// <exception cref="PersistingRuntimeException">Error is occured during reload object data.</exception>
        /// <exception cref="ParameterTypeNotSupportedException">Parameters of specified type not supported.</exception>
        /// <exception cref="PersistenceOperationIsNotSupportedException">Operation is not supported because of configuration part responsible for this operation is missing or does not support specific functionallity.</exception>
        void ReloadCollection<TInstance, TParameter>(IEnumerable<TInstance> collection, TParameter parameter)
            where TInstance : class
            where TParameter : class;

        /// <summary>
        ///     <para>
        /// Saves changes made in <paramref name="instance"/> in data storage and invokes saving instance's aggregates.
        /// If instance was previously marked as deleted, method removes it from data storage.
        /// </para>
        ///     <para>
        /// If instance supports deffer loading and is not loaded yet, method does not perform any
        /// actions, because changes can not be made without instance loading.
        /// </para>
        ///     <para>
        /// Method does not accept instance changes, but types implements current method should consider possibility
        /// of calling this method few times with same instance, in case of cycle references, for example.
        /// Repeated calls should not cause repeated saving, so method should take steps to avoid repeated saving.
        /// For example, instance may internally maintain list of saved instances, checks this list on saving and
        /// clear this list in see cref="IRepository{TInstance}.AcceptChanges" method.
        /// </para>
        /// </summary>
        /// <typeparam name="TInstance">The type of the instance.</typeparam>
        /// <param name="instance">The instance to save changes.</param>
        /// <exception cref="PersistingRuntimeException">Error occured while save object.</exception>
        /// <exception cref="VersionConflictException">Version conflict occured during save object.</exception>
        /// <exception cref="IdentityReferenceIntegrityException">Different instance with same identifier already registered.</exception>
        /// <exception cref="PersistenceOperationIsNotSupportedException">Operation is not supported because of configuration part responsible for this operation is missing or does not support specific functionallity.</exception>
        void Save<TInstance>(TInstance instance)
            where TInstance : class;

        /// <summary>
        /// 	<para>
        /// Saves changes made in each item of collection to data storage and invokes saving instance's aggregates.
        /// If instance was previously marked as deleted, method removes it from data storage.
        /// Also method saves instances were removed from collection, if collection type persister supports this feature.
        /// </para>
        /// 	<para>
        /// If collection supports deffer loading and is not loaded yet, method does not perform any
        /// actions, because changes can not be made without collection loading.
        /// </para>
        /// 	<para>
        /// Method does not accept collection changes, but types implements current method should consider possibility
        /// of calling this method few times with same instance, in case of cycle references, for example.
        /// Repeated calls should not cause repeated saving, so method should take steps to avoid repeated saving.
        /// For example, instance may internally maintain list of saved instances, checks this list on saving and
        /// clear this list in see cref="IRepository{TInstance}.AcceptChanges" method.
        /// </para>
        /// </summary>
        /// <typeparam name="TInstance">The type of the instance.</typeparam>
        /// <param name="collection">The collection.</param>
        /// <exception cref="PersistingRuntimeException">Error occured while save object.</exception>
        /// <exception cref="VersionConflictException">Version conflict occured during save object.</exception>
        /// <exception cref="IdentityReferenceIntegrityException">Different instance with same identifier already registered.</exception>
        /// <exception cref="PersistenceOperationIsNotSupportedException">Operation is not supported because of configuration part responsible for this operation is missing or does not support specific functionallity.</exception>
        void SaveCollection<TInstance>(IEnumerable<TInstance> collection)
            where TInstance : class;        

        /// <summary>
        /// Marks <paramref name="instance"/> as deleted.
        /// </summary>
        /// <typeparam name="TInstance">The type of the instance.</typeparam>
        /// <param name="instance">The instance to mark as deleted.</param>
        /// <remarks>
        /// Method itself does not save any changes to storage. For actually delete <paramref name="instance"/>
        /// from storage, call <see cref="IRepository{TInstance}.Save"/> method.
        /// </remarks>
        /// <exception cref="IdentityReferenceIntegrityException">Different instance with same identifier already registered.</exception>
        /// <exception cref="PersistingRuntimeException">Error occured while mark object as deleted.</exception>
        /// <exception cref="PersistenceOperationIsNotSupportedException">Operation is not supported because of configuration part responsible for this operation is missing or does not support specific functionallity.</exception>
        void MarkAsDeleted<TInstance>(TInstance instance)
            where TInstance : class;

        /// <summary>
        /// Marks every item in collection as deleted. Method does not clears collection content.
        /// </summary>
        /// <typeparam name="TInstance">The type of the instance.</typeparam>
        /// <param name="collection">The collection.</param>
        /// <remarks>
        /// Method itself does not save any changes to storage. For actually delete <paramref name="instance"/>
        /// from storage, call <see cref="IRepository{TInstance}.Save"/> method.
        /// </remarks>
        /// <exception cref="IdentityReferenceIntegrityException">Different instance with same identifier already registered.</exception>
        /// <exception cref="PersistingRuntimeException">Error occured while mark object as deleted.</exception>
        /// <exception cref="PersistenceOperationIsNotSupportedException">Operation is not supported because of configuration part responsible for this operation is missing or does not support specific functionallity.</exception>
        void MarkCollectionAsDeleted<TInstance>(IEnumerable<TInstance> collection)
            where TInstance : class;

        /// <summary>
        /// Marks <paramref name="instance"/> as needed to be physically saved to storage even
        /// if <paramref name="instance"/> data are not changed.
        /// This method may be called any times untill AcceptChanges method called, but instance will be saved only once.
        /// </summary>
        /// <typeparam name="TInstance">Type of instance.</typeparam>
        /// <param name="instance">Instance to mark as dirty.</param>
        /// <remarks>
        /// Use this method in couple with optimistic locking (version checking) to organize object hierarchy into 'atom' group.
        /// Call this method on successful instance saving using other members of 'atom' object group, then save these objects.
        /// </remarks>
        /// <exception cref="IdentityReferenceIntegrityException">Different instance with the same identifier already registered.</exception>
        /// <exception cref="PersistingRuntimeException">Error occured while mark object as dirty.</exception>
        /// <exception cref="PersistenceOperationIsNotSupportedException">Operation is not supported because of configuration part responsible for this operation is missing or does not support specific functionallity.</exception>
        void MarkAsDirty<TInstance>(TInstance instance)
            where TInstance : class;

        /// <summary>
        /// Marks every item in collection as needed to be physically saved to storage even if item data are not changed.
        /// This method may be called any times untill AcceptChanges method called, but instance will be saved only once.
        /// </summary>
        /// <typeparam name="TInstance">Type of instance.</typeparam>
        /// <param name="collection">The collection.</param>
        /// <remarks>
        /// Use this method in couple with optimistic locking (version checking) to organize objects hierarchy into 'atom' group.
        /// Call this method on successful instance saving using other members of 'atom' object group, then save these objects.
        /// </remarks>
        /// <exception cref="IdentityReferenceIntegrityException">Different instance with the same identifier already registered.</exception>
        /// <exception cref="PersistingRuntimeException">Error occured while mark object as dirty.</exception>
        /// <exception cref="PersistenceOperationIsNotSupportedException">Operation is not supported because of configuration part responsible for this operation is missing or does not support specific functionallity.</exception>
        void MarkCollectionAsDirty<TInstance>(IEnumerable<TInstance> collection)
            where TInstance : class;

        /// <summary>
        /// 	<para> Registers <paramref name="instance"/> in repository, if it is not registered yet. After registration
        /// <paramref name="instance"/> become available by its identifier. </para>
        /// 	<para> If same instance already registered in repository, method does not perform any action.</para>
        /// </summary>
        /// <typeparam name="TInstance">The type of the instance.</typeparam>
        /// <param name="instance">The instance to registers in repository.</param>
        /// <exception cref="IdentityReferenceIntegrityException">Different instance with same identifier already registered.</exception>
        /// <exception cref="PersistingRuntimeException">Error occured during add object.</exception>
        /// <exception cref="PersistenceOperationIsNotSupportedException">Operation is not supported because of configuration part responsible for this operation is missing or does not support specific functionallity.</exception>
        void Add<TInstance>(TInstance instance)
            where TInstance : class;

        /// <summary>
        /// 	<para> Registers every item of collection in session , if it is not registered yet.
        /// After registration item become available by its identifier. </para>
        /// 	<para> If same instance already registered in repository, method does not perform any action.</para>
        /// </summary>
        /// <typeparam name="TInstance">The type of the instance.</typeparam>
        /// <param name="collection">The collection.</param>
        /// <exception cref="IdentityReferenceIntegrityException">Different instance with same identifier already registered.</exception>
        /// <exception cref="PersistingRuntimeException">Error occured during add object.</exception>
        /// <exception cref="PersistenceOperationIsNotSupportedException">Operation is not supported because of configuration part responsible for this operation is missing or does not support specific functionallity.</exception>
        void AddCollection<TInstance>(IEnumerable<TInstance> collection)
            where TInstance : class;

        /// <summary>
        /// Joins specified instance or collection to repository if required, applies changes made to <paramref name="instance"/> and attach aggregates of specified instance.
        /// If specified instance is collection, attach each items and deletes items deleted from collection since detaching.
        /// </summary>
        /// <typeparam name="TInstance">The type of the instance.</typeparam>
        /// <param name="instance">The instance to attach.</param>
        /// <returns>
        /// The instance registered in repository contains data of <paramref name="instance"/>.
        /// </returns>
        /// <exception cref="PersistingRuntimeException">Error occured during Attach object.</exception>
        /// <exception cref="PersistenceOperationIsNotSupportedException">Operation is not supported because of configuration part responsible for this operation is missing or does not support specific functionallity.</exception>
        TInstance Attach<TInstance>(TInstance instance)
            where TInstance : class;

        /// <summary>
        /// Joins every item of collection to repository if required, applies changes made to <paramref name="instance"/> and attach aggregates of specified instance.
        /// If specified instance is collection, attach each items and deletes items deleted from collection since detaching.
        /// </summary>
        /// <typeparam name="TInstance">The type of the instance.</typeparam>
        /// <param name="collection">The collection.</param>
        /// <returns>
        /// The instance registered in repository contains data of <paramref name="instance"/>.
        /// </returns>
        /// <exception cref="PersistingRuntimeException">Error occured during Attach object.</exception>
        /// <exception cref="PersistenceOperationIsNotSupportedException">Operation is not supported because of configuration part responsible for this operation is missing or does not support specific functionallity.</exception>
        IEnumerable<TInstance> AttachCollection<TInstance>(IEnumerable<TInstance> collection)
            where TInstance : class;

        #endregion

        /// <summary>
        /// Get object registered in session.
        /// If object is not found, the object will be constructed, registered in session and reloaded
        /// using data passed as <paramref name="data"/> argument.
        /// If <paramref name="data"/> is null, the object will be constructed using data extracted from default gateway.
        /// </summary>
        /// <typeparam name="TInstance">The type of the instance.</typeparam>
        /// <typeparam name="TIdentifier">The type of the identifier.</typeparam>
        /// <param name="identifier">The identifier of instance to get.</param>
        /// <param name="data">The data used for object construction or null for using data from default gateway.</param>
        /// <returns>The instance with specified identifier.</returns>
        /// <exception cref="PersistingRuntimeException">Error occured during get object by identifier.</exception>
        TInstance GetInstance<TInstance, TIdentifier>(TIdentifier identifier, TypedRecord data)
            where TInstance : class;

        #region Reader members

        /// <summary>
        /// Return enumerator throught object sequence.
        /// </summary>
        /// <typeparam name="TItem">The type of the item.</typeparam>
        /// <typeparam name="TParameter">The type of the parameter for command.</typeparam>
        /// <param name="parameter">The parameter for command.</param>
        /// <returns>
        /// The sequence of object read from data storage.
        /// </returns>
        /// <exception cref="PersistingRuntimeException">Error occured while use object reader.</exception>
        /// <exception cref="ParameterTypeNotSupportedException">Parameter with specified type is not supported.</exception>
        IEnumerable<TItem> ExecuteObjectReader<TItem, TParameter>(TParameter parameter)
            where TItem : class
            where TParameter : class;

        /// <summary>
        /// Return enumerator throught object set.
        /// </summary>
        /// <typeparam name="TItem">The type of the item.</typeparam>
        /// <returns>
        /// The sequence of object read from data storage.
        /// </returns>
        /// <exception cref="PersistingRuntimeException">Error occured during creating object reader.</exception>
        IEnumerable<TItem> ExecuteObjectReader<TItem>()
            where TItem : class;

        #endregion

        /// <summary>
        ///     <para>
        /// Saves changes made in every instace registered in data storage and invokes saving instance's aggregates.
        /// If instance was previously marked as deleted, method removes it from data storage. </para>
        /// </summary>
        /// <exception cref="PersistingRuntimeException">Error occured while save object.</exception>
        /// <exception cref="VersionConflictException">Version conflict occured during save object.</exception>
        /// <exception cref="IdentityReferenceIntegrityException">Different instance with same identifier already registered.</exception>
        /// <exception cref="PersistenceOperationIsNotSupportedException">Operation is not supported because of configuration part responsible for this operation is missing or does not support specific functionallity.</exception>
        void SaveAll();
    }
}